package crud.service.impl;

//JDK库

import java.util.List;
import java.util.Objects;
//工具库
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
//框架库


import crud.Application;
import crud.verify.ParamVerify;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
//内部包
import apijson.framework.APIJSONController;
import crud.domain.CrudResult;
import crud.domain.SysInterface;
import crud.template.JSONTemplate;
import crud.service.JSONCrudService;

import javax.servlet.http.HttpSession;

@Service
@Slf4j
public class JSONCrudServiceImpl implements JSONCrudService {

    @Autowired
    HttpSession session;

    String dbType;

    private String getDbType() {
        if (dbType == null) {
            DruidDataSource ds = Application.getApplicationContext().getBean(DruidDataSource.class);
            dbType = ds.getDbType();
        }

        return dbType;
    }

    private boolean doInsetCheck(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl, SysInterface apiConfig) {
        String primaryFields = apiConfig.getPrimaryFields();
        if (StrUtil.isBlank(primaryFields)) {
            return true;
        }
        for (String tableName : jsonTemplate.keySet()) {
            if (Character.isUpperCase(tableName.charAt(0))) {
                JSONObject tableNode = jsonTemplate.getJSONObject(tableName);

                JSONObject primaryKeyField = new JSONObject(true);
                for (String fieldName : primaryFields.split(",")) {
                    primaryKeyField.put(fieldName, reqBody.get(fieldName));
                }

                JSONObject addCheckParam = new JSONObject(true);
                addCheckParam.put(tableName, primaryKeyField);
                addCheckParam.put("@datasource", jsonTemplate.getString("@datasource"));
                addCheckParam.put("@database", getDbType());
                addCheckParam.put("@schema", tableNode.getString("@schema"));

                String request = addCheckParam.toJSONString();
                String logText = String.format("addCheck,get,%s", request);
                log.info(logText);

                String getResBody = apiJSONCtrl.get(request, session);

                log.info(getResBody);
                JSONObject jsonResult = JSON.parseObject(getResBody);
                JSONObject row = jsonResult.getJSONObject(tableName);
                return row == null;
            }
        }

        return true;
    }

    private boolean doEditCheck(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl, SysInterface apiConfig) {
        String primaryFields = apiConfig.getPrimaryFields();
        if (StrUtil.isBlank(primaryFields)) {
            return true;
        }

        String idField = apiConfig.getIdField(); //ID字段
        if (idField == null) {
            idField = "id";
        }

        for (String tableName : jsonTemplate.keySet()) {
            if (Character.isUpperCase(tableName.charAt(0))) {

                JSONObject tableNode = jsonTemplate.getJSONObject(tableName);
                JSONObject primaryKeyField = new JSONObject(true);
                primaryKeyField.put(idField + "!", reqBody.getString(idField));
                for (String fieldName : primaryFields.split(",")) {
                    primaryKeyField.put(fieldName, reqBody.get(fieldName));
                }

                JSONObject addCheckParam = new JSONObject(true);
                addCheckParam.put(tableName, primaryKeyField);
                addCheckParam.put("@datasource", jsonTemplate.getString("@datasource"));
                addCheckParam.put("@database", getDbType());
                addCheckParam.put("@schema", tableNode.getString("@schema"));

                String request = addCheckParam.toJSONString();
                String logText = String.format("editCheck,get,%s", request);
                log.info(logText);

                String getResBody = apiJSONCtrl.get(request, session);

                JSONObject jsonResult = JSON.parseObject(getResBody);
                JSONObject row = jsonResult.getJSONObject(tableName);
                return row == null;

            }
        }


        return true;
    }

    private CrudResult doAdd(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl, SysInterface apiConfig) throws Exception {
        String requiredFields = apiConfig.getRequiredFields();
        if (requiredFields != null) {
            ParamVerify.requiredFieldInsertCheck(reqBody, requiredFields.split(","));
        }

        //重复记录检查
        if (!doInsetCheck(reqBody, jsonTemplate, apiJSONCtrl, apiConfig)) {
            throw new Exception("存在重复记录项");
        }

        //编译请求体
        JSONObject addReqParam = JSONTemplate.buildAddTemplate(jsonTemplate, reqBody);

        //调用CRUD方法
        String request = addReqParam.toJSONString();
        String logText = String.format("add,%s", request);
        log.info(logText);

        JSONObject data = new JSONObject();
        JSONObject addResult = JSON.parseObject(apiJSONCtrl.post(request, session), Feature.OrderedField);

        for (String key : addResult.keySet()) {
            if (!Character.isUpperCase(key.charAt(0))) {
                continue;
            }

            JSONObject node = addResult.getJSONObject(key);
            //对数据库有修改的操作只取ID字段
            data.put("id", node.getString("id"));
        }

        CrudResult crudResult = new CrudResult();
        crudResult.setStatus(addResult.getIntValue("code") == 200 ? 0 : -1);
        crudResult.setMessage(addResult.getString("msg"));
        crudResult.setData(data);

        return crudResult;
    }

    private CrudResult doDel(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl) throws Exception {


        //编译请求体
        JSONObject delReqParam = JSONTemplate.buildDeleteTemplate(jsonTemplate, reqBody);

        //调用CRUD方法
        String request = delReqParam.toJSONString();
        String logText = String.format("delete,%s", request);
        log.info(logText);

        JSONObject data = new JSONObject();
        JSONObject addResult = JSON.parseObject(apiJSONCtrl.delete(request, session), Feature.OrderedField);

        for (String key : addResult.keySet()) {
            if (!Character.isUpperCase(key.charAt(0))) {
                continue;
            }

            JSONObject node = addResult.getJSONObject(key);
            //对数据库有修改的操作只取ID字段
            data.put("id", node.getString("id"));
        }

        CrudResult crudResult = new CrudResult();
        crudResult.setStatus(addResult.getIntValue("code") == 200 ? 0 : -1);
        crudResult.setMessage(addResult.getString("msg"));
        crudResult.setData(data);

        return crudResult;
    }


    private CrudResult doEdit(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl, SysInterface apiConfig) throws Exception {
        String requiredFields = apiConfig.getRequiredFields();
        if (requiredFields != null) {
            ParamVerify.requiredFieldEditCheck(reqBody, requiredFields.split(","));
        }

        //重复记录检查
        if (!doEditCheck(reqBody, jsonTemplate, apiJSONCtrl, apiConfig)) {
            throw new Exception("存在重复记录项");
        }

        //编译请求体
        JSONObject editReqParam = JSONTemplate.buildEditTemplate(jsonTemplate, reqBody);

        //调用CRUD方法
        String request = editReqParam.toJSONString();
        String logText = String.format("edit,%s", request);
        log.info(logText);

        JSONObject data = new JSONObject();
        JSONObject editResult = JSON.parseObject(apiJSONCtrl.put(request, session), Feature.OrderedField);

        for (String key : editResult.keySet()) {
            if (!Character.isUpperCase(key.charAt(0))) {
                continue;
            }

            JSONObject node = editResult.getJSONObject(key);
            data.put("id", node.getString("id")); //对数据库有修改的操作只取ID字段
        }

        CrudResult crudResult = new CrudResult();
        crudResult.setStatus(editResult.getIntValue("code") == 200 ? 0 : -1);
        crudResult.setMessage(editResult.getString("msg"));
        crudResult.setData(data);

        return crudResult;
    }

    //原始查询
    private CrudResult doGet(JSONObject reqBody, JSONObject jsonTemplate, String apiOperType, APIJSONController<Long> apiJSONCtrl) throws Exception {

        //查询集合 处理分页
        if (jsonTemplate.containsKey("[]")) {
            JSONObject queryTemplate = jsonTemplate.getJSONObject("[]");

            //APIJSON分页 页码 从0开始 外部传入分页从1开始
            if (queryTemplate.containsKey("page")) {
                if (reqBody.containsKey("page")) {
                    int page = reqBody.getIntValue("page");
                    queryTemplate.put("page", page - 1);
                    reqBody.remove("page");
                } else {
                    queryTemplate.put("page", 0);
                }
            } else {
                queryTemplate.put("page", 0);
            }

            //apijson每页分页参数 count，外部传入分页参数perPage
            if (queryTemplate.containsKey("perPage")) {
                if (reqBody.containsKey("perPage")) {
                    int perPage = reqBody.getIntValue("perPage");
                    queryTemplate.put("count", perPage);
                    reqBody.remove("perPage");
                } else {
                    queryTemplate.put("count", 10);
                }
                queryTemplate.remove("perPage");
            } else {
                queryTemplate.put("count", 10);
            }
        }

        JSONObject getRequestJSON = new JSONObject();
        if (Objects.equals(apiOperType, "GET")) {
            getRequestJSON = JSONTemplate.buildGetTemplate(jsonTemplate, reqBody);
        } else if (Objects.equals(apiOperType, "LIST")) {
            getRequestJSON = JSONTemplate.buildListTemplate(jsonTemplate, reqBody);
        } else if (Objects.equals(apiOperType, "SELECT")) {
            getRequestJSON = JSONTemplate.buildListTemplate(jsonTemplate, reqBody);
        } else if (Objects.equals(apiOperType, "TREE")) {
            getRequestJSON = JSONTemplate.buildListTemplate(jsonTemplate, reqBody);
        }

        String request = getRequestJSON.toJSONString();
        String logText = String.format("get,%s", request);
        log.info(logText);
        int page = jsonTemplate.getJSONObject("[]").getInteger("page");
        int perPage = jsonTemplate.getJSONObject("[]").getInteger("count");


        JSONObject data = new JSONObject();
        data.put("page", page + 1);
        data.put("count", 0);
        data.put("rows", new JSONArray());

        JSONObject listResult = JSON.parseObject(apiJSONCtrl.get(request, session), Feature.OrderedField);
        if (listResult.containsKey("[]")) { //记录集查询

            int index = page * perPage;

            JSONArray rows = new JSONArray();
            Object rowsObject = listResult.get("[]");
            if (rowsObject instanceof JSONArray) {
                for (Object item : listResult.getJSONArray("[]")) {
                    JSONObject tmpRow = (JSONObject) item;
                    JSONObject row = new JSONObject(true);
                    for (String key : tmpRow.keySet()) {
                        JSONObject i = tmpRow.getJSONObject(key);
                        row.putAll(i);
                        row.put("_index", index + 1);
                        index += 1;
                    }
                    rows.add(row);
                }
            } else {
                //APIJSON查询失败 返回错误描述
                throw new Exception(listResult.toJSONString());
            }

            data.put("page", page + 1);
            data.put("count", listResult.getIntValue("total"));
            data.put("rows", rows);
        }

        CrudResult crudResult = new CrudResult();
        crudResult.setStatus(listResult.getIntValue("code") == 200 ? 0 : -1);
        crudResult.setMessage(listResult.getString("msg"));
        crudResult.setData(data);

        return crudResult;
    }

    //单条查询
    private CrudResult doRow(JSONObject reqBody, JSONObject jsonTemplate, APIJSONController<Long> apiJSONCtrl) throws Exception {

        //编译请求体
        JSONObject rowReqParam = JSONTemplate.buildRowTemplate(jsonTemplate, reqBody);

        //调用CRUD方法
        String request = rowReqParam.toJSONString();
        String logText = String.format("row,%s", request);
        log.info(logText);

        JSONObject data = new JSONObject();
        JSONObject rowResult = JSON.parseObject(apiJSONCtrl.get(request, session), Feature.OrderedField);
        //单条记录查询
        for (String key : rowResult.keySet()) {
            if (!Character.isUpperCase(key.charAt(0))) {
                continue;
            }
            JSONObject i = rowResult.getJSONObject(key);
            data.putAll(i);
        }
        CrudResult crudResult = new CrudResult();
        crudResult.setStatus(rowResult.getIntValue("code") == 200 ? 0 : -1);
        crudResult.setMessage(rowResult.getString("msg"));
        crudResult.setData(data);

        return crudResult;

    }

    private JSONArray toTreeData(JSONObject getResData) throws Exception {
        JSONArray data = new JSONArray();
        JSONArray rows = getResData.getJSONArray("rows");
        List<TreeNode<String>> nodeList = CollUtil.newArrayList();
        for (int index = 0; index < rows.size(); index++) {
            JSONObject row = rows.getJSONObject(index);

            String id = row.getString("id");
            String parentId = row.getString("parentId");
            String name = row.getString("label");
            String weight = row.getString("weight");
            nodeList.add(new TreeNode<>(id, parentId, name, weight));
        }
        TreeNodeConfig config = new TreeNodeConfig();
        config.setNameKey("label");
        config.setIdKey("id");
        config.setChildrenKey("children");

        List<Tree<String>> treeList = TreeUtil.build(nodeList, "0", config, (treeNode, tree) -> {
            tree.setId(treeNode.getId());
            tree.setName(treeNode.getName());
            tree.setParentId(treeNode.getParentId());
            tree.setWeight(treeNode.getWeight());
        });

        String strTreeList = JSON.toJSONString(treeList);
        data.add(JSON.parseArray(strTreeList));
        return data;
    }


    //执行CRUD操作
    @Override
    public CrudResult doCrud(JSONObject reqBody, SysInterface apiConfig, APIJSONController<Long> apiJSONCtrl) throws Exception {

        String apiBody = apiConfig.getApiTemplate();
        JSONObject jsonTemplate = JSONObject.parseObject(apiBody, Feature.OrderedField); //保持原始json顺序

        //配置数据源、数据库类型、默认库
        jsonTemplate.put("@datasource", apiConfig.getDatabaseId());
        jsonTemplate.put("@database", getDbType());
        if (!jsonTemplate.containsKey("@schema")) {
            String defaultSchema = apiConfig.getDatabaseSchema();
            if (defaultSchema != null && !defaultSchema.isEmpty()) {
                jsonTemplate.put("@schema", defaultSchema);
            }
        }

        CrudResult data = new CrudResult();

        String apiActionType = apiConfig.getApiActionType();
        if (Objects.equals(apiActionType, "ADD")) {
            data = doAdd(reqBody, jsonTemplate, apiJSONCtrl, apiConfig);
        } else if (Objects.equals(apiActionType, "DELETE")) {
            data = doDel(reqBody, jsonTemplate, apiJSONCtrl);
        } else if (Objects.equals(apiActionType, "EDIT")) {
            data = doEdit(reqBody, jsonTemplate, apiJSONCtrl, apiConfig);
        } else if (Objects.equals(apiActionType, "GET")) {
            data = doGet(reqBody, jsonTemplate, "GET", apiJSONCtrl);
        } else if (Objects.equals(apiActionType, "LIST")) {
            data = doGet(reqBody, jsonTemplate, "LIST", apiJSONCtrl);
        } else if (Objects.equals(apiActionType, "ROW")) {
            data = doRow(reqBody, jsonTemplate, apiJSONCtrl);
        } else if (Objects.equals(apiActionType, "SELECT")) {
            CrudResult midCrudResult = doGet(reqBody, jsonTemplate, "SELECT", apiJSONCtrl);
            data.setStatus(midCrudResult.getStatus());
            data.setMessage(midCrudResult.getMessage());
            data.setData(((JSONObject) midCrudResult.getData()).get("rows"));
        } else if (Objects.equals(apiActionType, "TREE")) {
            CrudResult midCrudResult = doGet(reqBody, jsonTemplate, "TREE", apiJSONCtrl);
            JSONArray treeData = toTreeData((JSONObject) midCrudResult.getData());
            data.setStatus(midCrudResult.getStatus());
            data.setMessage(midCrudResult.getMessage());
            data.setData(treeData);
        } else {
            data.setStatus(1);
            data.setMessage(String.format("未支持操作类型[JSON][%s]", apiActionType));
            data.setData(new JSONObject());
        }

        return data;
    }

}